Практическое задание к уроку 1 (2 неделя).

Линейная регрессия: переобучение и регуляризация

В этом задании мы на примерах увидим, как переобучаются линейные модели, разберем, почему так происходит, и выясним, как диагностировать и контролировать переобучение.

Во всех ячейках, где написан комментарий с инструкциями, нужно написать код, выполняющий эти инструкции. Остальные ячейки с кодом (без комментариев) нужно просто выполнить. Кроме того, в задании требуется отвечать на вопросы; ответы нужно вписывать после выделенного слова "Ответ:".

Напоминаем, что посмотреть справку любого метода или функции (узнать, какие у нее аргументы и что она делает) можно с помощью комбинации Shift+Tab. Нажатие Tab после имени объекта и точки позволяет посмотреть, какие методы и переменные есть у этого объекта.


In [2]:
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
%matplotlib inline

Мы будем работать с датасетом "bikes_rent.csv", в котором по дням записаны календарная информация и погодные условия, характеризующие автоматизированные пункты проката велосипедов, а также число прокатов в этот день. Последнее мы будем предсказывать; таким образом, мы будем решать задачу регрессии.

Знакомство с данными

Загрузите датасет с помощью функции pandas.read_csv в переменную df. Выведите первые 5 строчек, чтобы убедиться в корректном считывании данных:


In [3]:
# (0 баллов)
# Считайте данные и выведите первые 5 строк
df = pd.read_csv('bikes_rent.csv')
df.head()


Out[3]:
season yr mnth holiday weekday workingday weathersit temp atemp hum windspeed(mph) windspeed(ms) cnt
0 1 0 1 0 6 0 2 14.110847 18.18125 80.5833 10.749882 4.805490 985
1 1 0 1 0 0 0 2 14.902598 17.68695 69.6087 16.652113 7.443949 801
2 1 0 1 0 1 1 1 8.050924 9.47025 43.7273 16.636703 7.437060 1349
3 1 0 1 0 2 1 1 8.200000 10.60610 59.0435 10.739832 4.800998 1562
4 1 0 1 0 3 1 1 9.305237 11.46350 43.6957 12.522300 5.597810 1600

Для каждого дня проката известны следующие признаки (как они были указаны в источнике данных):

  • season: 1 - весна, 2 - лето, 3 - осень, 4 - зима
  • yr: 0 - 2011, 1 - 2012
  • mnth: от 1 до 12
  • holiday: 0 - нет праздника, 1 - есть праздник
  • weekday: от 0 до 6
  • workingday: 0 - нерабочий день, 1 - рабочий день
  • weathersit: оценка благоприятности погоды от 1 (чистый, ясный день) до 4 (ливень, туман)
  • temp: температура в Цельсиях
  • atemp: температура по ощущениям в Цельсиях
  • hum: влажность
  • windspeed(mph): скорость ветра в милях в час
  • windspeed(ms): скорость ветра в метрах в секунду
  • cnt: количество арендованных велосипедов (это целевой признак, его мы будем предсказывать)

Итак, у нас есть вещественные, бинарные и номинальные (порядковые) признаки, и со всеми из них можно работать как с вещественными. С номинальныеми признаками тоже можно работать как с вещественными, потому что на них задан порядок. Давайте посмотрим на графиках, как целевой признак зависит от остальных


In [4]:
fig, axes = plt.subplots(nrows=3, ncols=4, figsize=(15, 10))
for idx, feature in enumerate(df.columns[:-1]):
    df.plot(feature, "cnt", subplots=True, kind="scatter", ax=axes[idx / 4, idx % 4])


Блок 1. Ответьте на вопросы (каждый 0.5 балла):

  1. Каков характер зависимости числа прокатов от месяца?
    • ответ: линейная
  2. Укажите один или два признака, от которых число прокатов скорее всего зависит линейно
    • ответ: день недели, месяц.

Давайте более строго оценим уровень линейной зависимости между признаками и целевой переменной. Хорошей мерой линейной зависимости между двумя векторами является корреляция Пирсона. В pandas ее можно посчитать с помощью двух методов датафрейма: corr и corrwith. Метод df.corr вычисляет матрицу корреляций всех признаков из датафрейма. Методу df.corrwith нужно подать еще один датафрейм в качестве аргумента, и тогда он посчитает попарные корреляции между признаками из df и этого датафрейма.


In [5]:
# Код 1.1 (0.5 балла)
# Посчитайте корреляции всех признаков, кроме последнего, с последним с помощью метода corrwith:
df_new = df.drop('cnt',axis = 1)
print 'Все кроме последнего: \n', df_new.corr()
print '\n Последний признак: \n', df.corrwith(df['cnt'])


Все кроме последнего: 
                  season        yr      mnth   holiday   weekday  workingday  \
season          1.000000 -0.001844  0.831440 -0.010537 -0.003080    0.012485   
yr             -0.001844  1.000000 -0.001792  0.007954 -0.005461   -0.002013   
mnth            0.831440 -0.001792  1.000000  0.019191  0.009509   -0.005901   
holiday        -0.010537  0.007954  0.019191  1.000000 -0.101960   -0.253023   
weekday        -0.003080 -0.005461  0.009509 -0.101960  1.000000    0.035790   
workingday      0.012485 -0.002013 -0.005901 -0.253023  0.035790    1.000000   
weathersit      0.019211 -0.048727  0.043528 -0.034627  0.031087    0.061200   
temp            0.334315  0.047604  0.220205 -0.028556 -0.000170    0.052660   
atemp           0.342876  0.046106  0.227459 -0.032507 -0.007537    0.052182   
hum             0.205445 -0.110651  0.222204 -0.015937 -0.052232    0.024327   
windspeed(mph) -0.229046 -0.011817 -0.207502  0.006292  0.014282   -0.018796   
windspeed(ms)  -0.229046 -0.011817 -0.207502  0.006292  0.014282   -0.018796   

                weathersit      temp     atemp       hum  windspeed(mph)  \
season            0.019211  0.334315  0.342876  0.205445       -0.229046   
yr               -0.048727  0.047604  0.046106 -0.110651       -0.011817   
mnth              0.043528  0.220205  0.227459  0.222204       -0.207502   
holiday          -0.034627 -0.028556 -0.032507 -0.015937        0.006292   
weekday           0.031087 -0.000170 -0.007537 -0.052232        0.014282   
workingday        0.061200  0.052660  0.052182  0.024327       -0.018796   
weathersit        1.000000 -0.120602 -0.121583  0.591045        0.039511   
temp             -0.120602  1.000000  0.991702  0.126963       -0.157944   
atemp            -0.121583  0.991702  1.000000  0.139988       -0.183643   
hum               0.591045  0.126963  0.139988  1.000000       -0.248489   
windspeed(mph)    0.039511 -0.157944 -0.183643 -0.248489        1.000000   
windspeed(ms)     0.039511 -0.157944 -0.183643 -0.248489        1.000000   

                windspeed(ms)  
season              -0.229046  
yr                  -0.011817  
mnth                -0.207502  
holiday              0.006292  
weekday              0.014282  
workingday          -0.018796  
weathersit           0.039511  
temp                -0.157944  
atemp               -0.183643  
hum                 -0.248489  
windspeed(mph)       1.000000  
windspeed(ms)        1.000000  

 Последний признак: 
season            0.406100
yr                0.566710
mnth              0.279977
holiday          -0.068348
weekday           0.067443
workingday        0.061156
weathersit       -0.297391
temp              0.627494
atemp             0.631066
hum              -0.100659
windspeed(mph)   -0.234545
windspeed(ms)    -0.234545
cnt               1.000000
dtype: float64

В выборке есть признаки, коррелирующие с целевым, а значит, задачу можно решать линейными методами.

По графикам видно, что некоторые признаки похожи друг на друга. Поэтому давайте также посчитаем корреляции между вещественными признаками.


In [6]:
# Код 1.2 (0.5 балла)
# Посчитайте попарные корреляции между признаками temp, atemp, hum, windspeed(mph), windspeed(ms) и cnt
# с помощью метода corr:
df_new = df.drop(['season', 'mnth','yr','holiday','weekday','workingday','weathersit'], axis=1)
df_new.corr()


Out[6]:
temp atemp hum windspeed(mph) windspeed(ms) cnt
temp 1.000000 0.991702 0.126963 -0.157944 -0.157944 0.627494
atemp 0.991702 1.000000 0.139988 -0.183643 -0.183643 0.631066
hum 0.126963 0.139988 1.000000 -0.248489 -0.248489 -0.100659
windspeed(mph) -0.157944 -0.183643 -0.248489 1.000000 1.000000 -0.234545
windspeed(ms) -0.157944 -0.183643 -0.248489 1.000000 1.000000 -0.234545
cnt 0.627494 0.631066 -0.100659 -0.234545 -0.234545 1.000000

На диагоналях, как и полагается, стоят единицы. Однако в матрице имеются еще две пары сильно коррелирующих столбцов: temp и atemp (коррелируют по своей природе) и два windspeed (потому что это просто перевод одних единиц в другие). Далее мы увидим, что этот факт негативно сказывается на обучении линейной модели.

Напоследок посмотрим средние признаков (метод mean), чтобы оценить масштаб признаков и доли 1 у бинарных признаков.


In [7]:
# Код 1.3 (0.5 балла)
# Выведите средние признаков
df.mean()


Out[7]:
season               2.496580
yr                   0.500684
mnth                 6.519836
holiday              0.028728
weekday              2.997264
workingday           0.683995
weathersit           1.395349
temp                20.310776
atemp               23.717699
hum                 62.789406
windspeed(mph)      12.762576
windspeed(ms)        5.705220
cnt               4504.348837
dtype: float64

Признаки имеют разный масштаб, значит для дальнейшей работы нам лучше нормировать матрицу объекты-признаки.

Проблема первая: коллинеарные признаки

Итак, в наших данных один признак дублирует другой, и есть еще два очень похожих. Конечно, мы могли бы сразу удалить дубликаты, но давайте посмотрим, как бы происходило обучение модели, если бы мы не заметили эту проблему.

Для начала проведем масштабирование, или стандартизацию признаков: из каждого признака вычтем его среднее и поделим на стандартное отклонение. Это можно сделать с помощью метода scale.

Кроме того, нужно перемешать выборку, это потребуется для кросс-валидации.


In [8]:
from sklearn.preprocessing import scale
from sklearn.utils import shuffle

In [9]:
df_shuffled = shuffle(df, random_state=123)
X = scale(df_shuffled[df_shuffled.columns[:-1]])
y = df_shuffled["cnt"]

Давайте обучим линейную регрессию на наших данных и посмотрим на веса признаков.


In [10]:
from sklearn.linear_model import LinearRegression

In [11]:
# Код 2.1 (1 балл)
# Создайте объект линейного регрессора, обучите его на всех данных и выведите веса модели 
# (веса хранятся в переменной coef_ класса регрессора).
# Можно выводить пары (название признака, вес), воспользовавшись функцией zip, встроенной в язык python
# Названия признаков хранятся в переменной df.columns
lin_reg = LinearRegression()
lin_reg.fit(X,y)
print zip(df.columns,lin_reg.coef_)


[('season', 570.87077517081241), ('yr', 1021.9689246859298), ('mnth', -141.30534047164508), ('holiday', -86.757528967927726), ('weekday', 137.22464485932142), ('workingday', 56.390851352823688), ('weathersit', -330.23219391326302), ('temp', 367.47441047339532), ('atemp', 585.55310452138167), ('hum', -145.60839851748983), ('windspeed(mph)', 12457394871096.072), ('windspeed(ms)', -12457394871294.529)]

Мы видим, что веса при линейно-зависимых признаках по модулю значительно больше, чем при других признаках.

Чтобы понять, почему так произошло, вспомним аналитическую формулу, по которой вычисляются веса линейной модели в методе наименьших квадратов:

$w = (X^TX)^{-1} X^T y$.

Если в X есть коллинеарные (линейно-зависимые) столбцы, матрица $X^TX$ становится вырожденной, и формула перестает быть корректной. Чем более зависимы признаки, тем меньше определитель этой матрицы и тем хуже аппроксимация $Xw \approx y$. Такая ситуацию называют проблемой мультиколлинеарности, вы обсуждали ее на лекции.

С парой temp-atemp чуть менее коррелирующих переменных такого не произошло, однако на практике всегда стоит внимательно следить за коэффициентами при похожих признаках.

Решение проблемы мультиколлинеарности состоит в регуляризации линейной модели. К оптимизируемому функционалу прибавляют L1 или L2 норму весов, умноженную на коэффициент регуляризации $\alpha$. В первом случае метод называется Lasso, а во втором --- Ridge. Подробнее об этом также рассказано в лекции.

Обучите регрессоры Ridge и Lasso с параметрами по умолчанию и убедитесь, что проблема с весами решилась.


In [12]:
from sklearn.linear_model import Lasso, Ridge

In [13]:
# Код 2.2 (0.5 балла)
# Обучите линейную модель с L1-регуляризацией и выведите веса
lass = Lasso()
lass.fit(X,y)
print zip(df.columns,lass.coef_)


[('season', 560.24161603088703), ('yr', 1019.4634940657196), ('mnth', -128.7306270367875), ('holiday', -86.152781333710962), ('weekday', 137.34789390496329), ('workingday', 55.212370641356678), ('weathersit', -332.36985696234882), ('temp', 376.3632362096987), ('atemp', 576.53079350455073), ('hum', -144.12915500348595), ('windspeed(mph)', -197.13968940248608), ('windspeed(ms)', -2.8049652690829766e-08)]

In [14]:
# Код 2.3 (0.5 балла)
# Обучите линейную модель с L2-регуляризацией и выведите веса
rid = Ridge()
rid.fit(X,y)
print zip(df.columns,rid.coef_)


[('season', 563.06457225201746), ('yr', 1018.9483787875288), ('mnth', -131.87332028247684), ('holiday', -86.746097997092576), ('weekday', 138.00511117871909), ('workingday', 55.903110375064436), ('weathersit', -332.34978849907213), ('temp', 386.45788919192034), ('atemp', 566.34704706005527), ('hum', -145.0713272986732), ('windspeed(mph)', -99.259441081715025), ('windspeed(ms)', -99.25944115441807)]

Проблема вторая: неинформативные признаки

В отличие от L2-регуляризации, L1 обнуляет веса при некоторых признаках. Объяснение данному факту дается в одной из лекций курса.

Давайте пронаблюдаем, как меняются веса при увеличении коэффициента регуляризации $\alpha$ (в лекции коэффициент при регуляризаторе мог быть обозначен другой буквой).


In [15]:
# Код 3.1 (1 балл)
alphas = np.arange(1, 500, 50)
coefs_lasso = np.zeros((alphas.shape[0], X.shape[1])) # матрица весов размера (число регрессоров) x (число признаков)
coefs_ridge = np.zeros((alphas.shape[0], X.shape[1]))
# Для каждого значения коэффициента из alphas обучите регрессор Lasso
# и запишите веса в соответствующую строку матрицы coefs_lasso (вспомните встроенную в python функцию enumerate),
# а затем обучите Ridge и запишите веса в coefs_ridge.
i=0
for alpha in alphas:
    for coef in enumerate(lass.coef_):
        lass=Lasso(alpha)
        lass.fit(X, y)
        coefs_lasso[i,coef[0]]=lass.coef_[coef[0]]
    i+=1
print 'Lasso\n',zip(coefs_lasso,alphas)
i=0
for alpha in alphas:
    for coef in enumerate(rid.coef_):
        rid=Ridge(alpha)
        rid.fit(X, y)
        coefs_ridge[i,coef[0]]=rid.coef_[coef[0]]
    i+=1
print 'Ridge\n',zip(coefs_ridge,alphas)


Lasso
[(array([  5.60241616e+02,   1.01946349e+03,  -1.28730627e+02,
        -8.61527813e+01,   1.37347894e+02,   5.52123706e+01,
        -3.32369857e+02,   3.76363236e+02,   5.76530794e+02,
        -1.44129155e+02,  -1.97139689e+02,  -2.80496527e-08]), 1), (array([  4.10969632e+02,   9.77019409e+02,  -0.00000000e+00,
        -5.34489688e+01,   9.19434374e+01,   1.75372118e+01,
        -3.18125568e+02,   3.22829934e+02,   6.10031512e+02,
        -9.10689615e+01,  -1.45066095e+02,  -2.29876665e-08]), 51), (array([  3.70077089e+02,   9.35945490e+02,   0.00000000e+00,
        -1.21619360e+01,   4.88886342e+01,   0.00000000e+00,
        -3.08805664e+02,   2.69417263e+02,   6.32502623e+02,
        -2.75042876e+01,  -9.37749037e+01,  -2.41641619e-08]), 101), (array([  3.32835717e+02,   8.91870058e+02,   0.00000000e+00,
        -0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
        -2.79616688e+02,   2.11052030e+02,   6.62920880e+02,
        -0.00000000e+00,  -5.01551472e+01,  -2.62774544e-08]), 151), (array([  2.98134448e+02,   8.45652857e+02,   0.00000000e+00,
        -0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
        -2.35571345e+02,   1.24144807e+02,   7.25379483e+02,
        -0.00000000e+00,  -1.26461769e+01,  -2.78783243e-08]), 201), (array([ 258.9272005 ,  799.23727252,    0.        ,   -0.        ,
          0.        ,    0.        , -190.82154925,   72.07593792,
        750.3631072 ,   -0.        ,   -0.        ,   -0.        ]), 251), (array([ 217.42751314,  752.72080848,    0.        ,   -0.        ,
          0.        ,    0.        , -145.71339843,   37.71536548,
        756.29686627,   -0.        ,   -0.        ,   -0.        ]), 301), (array([ 175.92984494,  706.20378802,    0.        ,   -0.        ,
          0.        ,    0.        , -100.60538939,    3.66093817,
        761.92633683,   -0.        ,   -0.        ,   -0.        ]), 351), (array([ 134.6284966 ,  659.63253472,    0.        ,   -0.        ,
          0.        ,    0.        ,  -55.51106328,    0.        ,
        737.34806057,   -0.        ,   -0.        ,   -0.        ]), 401), (array([  93.3517423 ,  613.05463922,    0.        ,   -0.        ,
          0.        ,    0.        ,  -10.41856902,    0.        ,
        709.13087706,   -0.        ,   -0.        ,   -0.        ]), 451)]
Ridge
[(array([  563.06457225,  1018.94837879,  -131.87332028,   -86.746098  ,
         138.00511118,    55.90311038,  -332.3497885 ,   386.45788919,
         566.34704706,  -145.0713273 ,   -99.25944108,   -99.25944115]), 1), (array([ 461.1786324 ,  954.30774189,  -41.56545001,  -84.91347027,
        126.60366663,   54.2516911 , -313.27515141,  458.90147709,
        481.44350031, -151.29101033, -101.62731482, -101.62731483]), 51), (array([ 403.97653045,  898.08362212,    5.67424346,  -81.91106159,
        117.94141144,   52.72848389, -298.40898086,  455.28986839,
        467.43141748, -152.68605059, -102.10167084, -102.10167084]), 101), (array([ 366.60423851,  848.46346471,   34.02728278,  -78.77196479,
        110.68012006,   51.25719208, -286.12515935,  447.47982533,
        455.75402966, -151.48320603, -102.00537592, -102.00537593]), 151), (array([ 339.74528956,  804.25104797,   52.48987295,  -75.71740756,
        104.40346414,   49.84189502, -275.48609488,  438.50968939,
        444.7642744 , -148.94396978, -101.58638458, -101.58638458]), 201), (array([ 319.15864133,  764.5614337 ,   65.15200737,  -72.81970821,
         98.87880449,   48.4845255 , -266.00255564,  429.21412336,
        434.23523472, -145.6981149 , -100.96471143, -100.96471143]), 251), (array([ 302.63606049,  728.70854018,   74.13828141,  -70.09898324,
         93.95581101,   47.18533838, -257.390629  ,  419.9261517 ,
        424.12192175, -142.08934531, -100.20862041, -100.20862041]), 301), (array([ 288.91327631,  696.14581646,   80.660457  ,  -67.55485237,
         89.52867543,   45.94346702, -249.47139541,  410.80018411,
        414.40937279, -138.31653824,  -99.36066117,  -99.36066117]), 351), (array([ 277.21321566,  666.42974721,   85.45925409,  -65.17870596,
         85.51866655,   44.75730348, -242.12439914,  401.91194457,
        405.08585263, -134.4990383 ,  -98.44906427,  -98.44906427]), 401), (array([ 267.03109023,  639.19536755,   89.01232285,  -62.95890597,
         81.86490933,   43.62477037, -235.26399722,  393.29798599,
        396.13815546, -130.70987223,  -97.49343362,  -97.49343362]), 451)]

Визуализируем динамику весов при увеличении параметра регуляризации:


In [16]:
plt.figure(figsize=(8, 5))
for coef, feature in zip(coefs_lasso.T, df.columns):
    plt.plot(alphas, coef, label=feature, color=np.random.rand(3))
plt.legend(loc="upper right", bbox_to_anchor=(1.4, 0.95))
plt.xlabel("alpha")
plt.ylabel("feature weight")
plt.title("Lasso")

plt.figure(figsize=(8, 5))
for coef, feature in zip(coefs_ridge.T, df.columns):
    plt.plot(alphas, coef, label=feature, color=np.random.rand(3))
plt.legend(loc="upper right", bbox_to_anchor=(1.4, 0.95))
plt.xlabel("alpha")
plt.ylabel("feature weight")
plt.title("Ridge")


Out[16]:
<matplotlib.text.Text at 0xf7fae80>

Ответы на следующие вопросы можно давать, глядя на графики или выводя коэффициенты на печать.

Блок 2. Ответьте на вопросы (каждый 0.25 балла):

  1. Какой регуляризатор (Ridge или Lasso) агрессивнее уменьшает веса при одном и том же alpha?
    • Ответ: Lass
  2. Что произойдет с весами Lasso, если alpha сделать очень большим? Поясните, почему так происходит.
    • Ответ: При большом alpha коэффициенты обнулятся из-за низкой предсказательной способности.
  3. Можно ли утверждать, что Lasso исключает один из признаков windspeed при любом значении alpha > 0? А Ridge? Ситается, что регуляризатор исключает признак, если коэффициент при нем < 1e-3.
    • Ответ: Да. Нет.
  4. Какой из регуляризаторов подойдет для отбора неинформативных признаков?
    • Ответ: Lasso

Далее будем работать с Lasso.

Итак, мы видим, что при изменении alpha модель по-разному подбирает коэффициенты признаков. Нам нужно выбрать наилучшее alpha.

Для этого, во-первых, нам нужна метрика качества. Будем использовать в качестве метрики сам оптимизируемый функционал метода наименьших квадратов, то есть Mean Square Error.

Во-вторых, нужно понять, на каких данных эту метрику считать. Нельзя выбирать alpha по значению MSE на обучающей выборке, потому что тогда мы не сможем оценить, как модель будет делать предсказания на новых для нее данных. Если мы выберем одно разбиение выборки на обучающую и тестовую (это называется holdout), то настроимся на конкретные "новые" данные, и вновь можем переобучиться. Поэтому будем делать несколько разбиений выборки, на каждом пробовать разные значения alpha, а затем усреднять MSE. Удобнее всего делать такие разбиения кросс-валидацией, то есть разделить выборку на K частей, или блоков, и каждый раз брать одну из них как тестовую, а из оставшихся блоков составлять обучающую выборку.

Делать кросс-валидацию для регрессии в sklearn совсем просто: для этого есть специальный регрессор, LassoCV, который берет на вход список из alpha и для каждого из них вычисляет MSE на кросс-валидации. После обучения (если оставить параметр cv=3 по умолчанию) регрессор будет содержать переменную mse_path_, матрицу размера len(alpha) x k, k = 3 (число блоков в кросс-валидации), содержащую значения MSE на тесте для соответствующих запусков. Кроме того, в переменной alpha_ будет храниться выбранное значение параметра регуляризации, а в coef_, традиционно, обученные веса, соответствующие этому alpha_.

Обратите внимание, что регрессор может менять порядок, в котором он проходит по alphas; для сопоставления с матрицей MSE лучше использовать переменную регрессора alphas_.


In [17]:
from sklearn.linear_model import LassoCV

In [18]:
# Код 3.2 (1 балл)
# Обучите регрессор LassoCV на всех параметрах регуляризации из alpha
# Постройте график _усредненного_ по строкам MSE в зависимости от alpha. 
# Выведите выбранное alpha, а также пары "признак-коэффициент" для обученного вектора коэффициентов
alphas = np.arange(1, 100, 5)
lass_cv=LassoCV(alphas=alphas)
lass_cv.fit(X,y)

print 'Выбранное alp = ',lass_cv.alpha_
print '\n Признак-коэфф: \n', zip(df.columns,lass_cv.coef_)

average = map(lambda elem: sum(elem) / 3, lass_cv.mse_path_[:])
plt.plot(lass_cv.alphas_, average)
plt.title('MSE & alphas')
plt.xlabel('alphas')
plt.ylabel('MSE')


Выбранное alp =  6

 Признак-коэфф: 
[('season', 532.0189828413537), ('yr', 1015.0602226430597), ('mnth', -100.03952614356626), ('holiday', -83.293959875299166), ('weekday', 132.50446549095795), ('workingday', 51.557085614073891), ('weathersit', -330.55985673998129), ('temp', 370.67985503003513), ('atemp', 581.39693106549669), ('hum', -140.00740550068869), ('windspeed(mph)', -191.77140847135064), ('windspeed(ms)', -2.6854518176296607e-08)]
Out[18]:
<matplotlib.text.Text at 0x1122df98>

Итак, мы выбрали некоторый параметр регуляризации. Давайте посмотрим, какие бы мы выбирали alpha, если бы делили выборку только один раз на обучающую и тестовую, то есть рассмотрим траектории MSE, соответствующие отдельным блокам выборки.


In [19]:
# Код 3.3 (1 балл)
# Выведите значения alpha, соответствующие минимумам MSE на каждом разбиении (то есть по столбцам).
# На трех отдельных графиках визуализируйте столбцы .mse_path_
min_mse = lass_cv.mse_path_.argmin(axis = 0)
print 'Минимумы mse на кождом разбиении  =  ', min_mse

def plt_mse(min_mse):
    plt.figure()
    plt.plot(lass_cv.alphas_, min_mse)
    plt.title("MSE")
    plt.xlabel("alpha")
    plt.ylabel("MSE")

mse_arr = lass_cv.mse_path_.T
for i in range(len(mse_arr)):
    plt_mse(mse_arr[i])


Минимумы mse на кождом разбиении  =   [11 18 19]

На каждом разбиении оптимальное значение alpha свое, и ему соответствует большое MSE на других разбиениях. Получается, что мы настраиваемся на конкретные обучающие и контрольные выборки. При выборе alpha на кросс-валидации мы выбираем нечто "среднее", что будет давать приемлемое значение метрики на разных разбиениях выборки.

Наконец, как принято в анализе данных, давайте проинтерпретируем результат.

Блок 3. Ответьте на вопросы (каждый 0.5 балла):

  1. В последней обученной модели выберите 4 признака с наибольшими (положительными) коэфициентами (и выпишите их), посмотрите на визуализации зависимостей cnt от этих признаков, которые мы рисовали в блоке "Знакомство с данными". Видна ли возрастающая линейная зависимость cnt от этих признаков по графикам? Логично ли утверждать (из здравого смысла), что чем больше значение этих признаков, тем больше людей захотят взять велосипеды?
    • Ответ: 'season' 532.0189828413537, 'yr' 1015.0602226430597, 'temp' 370.67985503003513, 'atemp' 581.39693106549669 На season и yr линейная зависимость видна. Не всегда можно прийти к такому выводу.
  2. Выберите 3 признака с наибольшими по модулю отрицательными коэффициентами (и выпишите их), посмотрите на соответствующие визуализации. Видна ли убывающая линейная зависимость? Логично ли утверждать, что чем больше величина этих признаков, тем меньше людей захотят взять велосипеды?
    • Ответ:'weathersit' -330.55985673998129,'hum' -140.00740550068869, 'windspeed(mph)' -191.77140847135064. На weathersit линейная зависимость видна. Не всегда можно прийти к выводу, что чем больше величина этих признаков, тем меньше спрос.
  3. Выпишите признаки с коэффициентами, близкими к нулю (< 1e-3). Как вы думаете, почему модель исключила их из модели (вновь посмотрите на графики)? Верно ли, что они никак не влияют на спрос на велосипеды?
    • Ответ:'windspeed(ms)', -2.6854518176296607e-08. Потому что графики ветра в милях/с и м/с имеют одинаковую структуру. Да, не влияют.

Заключение

Итак, мы посмотрели, как можно следить за адекватностью линейной модели, как отбирать признаки и как грамотно, по возможности не настраиваясь на какую-то конкретную порцию данных, подбирать коэффициент регуляризации.

Стоит отметить, что с помощью кросс-валидации удобно подбирать лишь небольшое число параметров (1, 2, максимум 3), потому что для каждой допустимой их комбинации нам приходится несколько раз обучать модель, а это времязатратный процесс, особенно если нужно обучаться на больших объемах данных.